This is an R Markdown Notebook. When you execute code within the notebook, the results appear beneath the code.

Try executing this chunk by clicking the Run button within the chunk or by placing your cursor inside it and pressing Ctrl+Shift+Enter.

# general visualisation
library('ggplot2') # visualisation
Registered S3 method overwritten by 'dplyr':
  method           from
  print.rowwise_df     
library('scales') # visualisation
library('patchwork') # visualisation
library('RColorBrewer') # visualisation
library('corrplot') # visualisation
corrplot 0.84 loaded
# general data manipulation
library('dplyr') # data manipulation

Attaching package: 㤼㸱dplyr㤼㸲

The following objects are masked from 㤼㸱package:stats㤼㸲:

    filter, lag

The following objects are masked from 㤼㸱package:base㤼㸲:

    intersect, setdiff, setequal, union
library('readr') # input/output

Attaching package: 㤼㸱readr㤼㸲

The following object is masked from 㤼㸱package:scales㤼㸲:

    col_factor
library('vroom') # input/output
library('skimr') # overview
Registered S3 methods overwritten by 'htmltools':
  method               from         
  print.html           tools:rstudio
  print.shiny.tag      tools:rstudio
  print.shiny.tag.list tools:rstudio
library('tibble') # data wrangling
library('tidyr') # data wrangling
library('purrr') # data wrangling

Attaching package: 㤼㸱purrr㤼㸲

The following object is masked from 㤼㸱package:scales㤼㸲:

    discard
library('stringr') # string manipulation
library('forcats') # factor manipulation

# specific visualisation
library('alluvial') # visualisation
library('ggrepel') # visualisation
library('ggforce') # visualisation
library('ggridges') # visualisation
library('gganimate') # animations
No renderer backend detected. gganimate will default to writing frames to separate files
Consider installing:
- the `gifski` package for gif output
- the `av` package for video output
and restarting the R session
library('GGally') # visualisation
Registered S3 method overwritten by 'GGally':
  method from   
  +.gg   ggplot2

Attaching package: 㤼㸱GGally㤼㸲

The following object is masked from 㤼㸱package:dplyr㤼㸲:

    nasa
library('ggthemes') # visualisation
library('wesanderson') # visualisation
library('kableExtra') # display

Attaching package: 㤼㸱kableExtra㤼㸲

The following object is masked from 㤼㸱package:dplyr㤼㸲:

    group_rows
# Date + forecast
library('lubridate') # date and time

Attaching package: 㤼㸱lubridate㤼㸲

The following objects are masked from 㤼㸱package:dplyr㤼㸲:

    intersect, setdiff, union

The following objects are masked from 㤼㸱package:base㤼㸲:

    date, intersect, setdiff, union
library('forecast') # time series analysis
Registered S3 method overwritten by 'quantmod':
  method            from
  as.zoo.data.frame zoo 
#library('prophet') # time series analysis
library('timetk') # time series analysis

# Interactivity
library('crosstalk')
library('plotly')
Registered S3 method overwritten by 'data.table':
  method           from
  print.data.table     
Registered S3 method overwritten by 'htmlwidgets':
  method           from         
  print.htmlwidget tools:rstudio

Attaching package: 㤼㸱plotly㤼㸲

The following object is masked from 㤼㸱package:ggplot2㤼㸲:

    last_plot

The following object is masked from 㤼㸱package:stats㤼㸲:

    filter

The following object is masked from 㤼㸱package:graphics㤼㸲:

    layout
# parallel
library('foreach')

Attaching package: 㤼㸱foreach㤼㸲

The following objects are masked from 㤼㸱package:purrr㤼㸲:

    accumulate, when
library('doParallel')
Loading required package: iterators
Loading required package: parallel
library(vroom)
library(stringr)
library(tidyverse)
Registered S3 methods overwritten by 'dbplyr':
  method         from
  print.tbl_lazy     
  print.tbl_sql      
train <- vroom(str_c('sales_train_validation.csv'), delim = ",", col_types = cols())
prices <- vroom(str_c('sell_prices.csv'), delim = ",", col_types = cols())
calendar <- read_csv(str_c('calendar.csv'), col_types = cols())

sample_submit <- vroom(str_c('sample_submission.csv'), delim = ",", col_types = cols())

Add a new chunk by clicking the Insert Chunk button on the toolbar or by pressing Ctrl+Alt+I.

When you save the notebook, an HTML file containing the code and output will be saved alongside it (click the Preview button or press Ctrl+Shift+K to preview the HTML file).

The preview shows you a rendered HTML copy of the contents of the editor. Consequently, unlike Knit, Preview does not run any R code chunks. Instead, the output of the chunk when it was last run in the editor is displayed.

extract_ts <- function(df){
  
  min_date <- as.Date("2011-01-29")
  
  df %>%
    select(id, starts_with("d_")) %>%  
    pivot_longer(starts_with("d_"), names_to = "dates", values_to = "sales") %>%
    mutate(dates = as.integer(str_remove(dates, "d_"))) %>% 
    mutate(dates = min_date + dates - 1) %>% 
    mutate(id = str_remove(id, "_validation"))
}

set.seed(4321)
foo <- train %>% 
  sample_n(50)

ts_out <- extract_ts(foo)

cols <- ts_out %>% 
  distinct(id) %>% 
  mutate(cols = rep_len(brewer.pal(7, "Set2"), length.out = n_distinct(ts_out$id)))

ts_out <- ts_out %>% 
  left_join(cols, by = "id")

pal <- cols$cols %>%
   setNames(cols$id)
shared_ts <- highlight_key(ts_out)

palette(brewer.pal(9, "Set3"))

gg <- shared_ts %>% 
  ggplot(aes(dates, sales, col = id, group = id)) +
  geom_line() +
  scale_color_manual(values = pal) +
  labs(x = "Date", y = "Sales") +
  theme_tufte() +
  NULL

filter <- bscols(
  filter_select("ids", "Sales over time: Select a time series ID (remove with backspace key, navigate with arrow keys):", shared_ts, ~id, multiple = TRUE),
  ggplotly(gg, dynamicTicks = TRUE),
  widths = c(12, 12)
)
Sum of bscol width units is greater than 12
bscols(filter)
foo <- train %>% 
  summarise_at(vars(starts_with("d_")), sum) %>% 
  mutate(id = 1)

bar <- extract_ts(foo)

gg <- bar %>% 
  ggplot(aes(dates, sales)) +
  geom_line(col = "blue") +
  theme_tufte() +
  labs(x = "Date", y = "Sales", title = "All aggregate sales")

ggplotly(gg, dynamicTicks = TRUE)
foo <- train %>% 
  summarise_at(vars(starts_with("d_")), sum) %>% 
  mutate(id = 1)

bar <- extract_ts(foo)

gg <- bar %>% 
  ggplot(aes(dates, sales)) +
  geom_line(col = "blue") +
  theme_tufte() +
  labs(x = "Date", y = "Sales", title = "All aggregate sales")

ggplotly(gg, dynamicTicks = TRUE)
foo <- train %>%
  group_by(state_id) %>% 
  summarise_at(vars(starts_with("d_")), sum) %>% 
  rename(id = state_id)

bar <- extract_ts(foo) %>% 
  mutate(month = month(dates),
         year = year(dates)) %>% 
  group_by(month, year, id) %>% 
  summarise(sales = sum(sales),
            dates = min(dates)) %>% 
  ungroup() %>% 
  filter(str_detect(as.character(dates), "..-..-01")) %>% 
  filter(dates != max(dates))

gg <- bar %>% 
  ggplot(aes(dates, sales, col = id)) +
  geom_line() +
  theme_tufte() +
  labs(x = "Date", y = "Sales", title = "Monthly Sales per State")

ggplotly(gg, dynamicTicks = TRUE)
foo <- train %>%
  group_by(cat_id) %>% 
  summarise_at(vars(starts_with("d_")), sum) %>% 
  rename(id = cat_id)

bar <- train %>%
  group_by(store_id) %>% 
  summarise_at(vars(starts_with("d_")), sum) %>% 
  rename(id = store_id)

p1 <- extract_ts(foo) %>% 
  mutate(month = month(dates),
         year = year(dates)) %>% 
  group_by(month, year, id) %>% 
  summarise(sales = sum(sales),
            dates = min(dates)) %>% 
  ungroup() %>% 
  filter(str_detect(as.character(dates), "..-..-01")) %>% 
  filter(dates != max(dates)) %>% 
  ggplot(aes(dates, sales, col = id)) +
  geom_line() +
  theme_hc() +
  theme(legend.position = "none") +
  labs(title = "Sales per Category", x = "Date", y = "Sales")

p2 <- train %>% 
  count(cat_id) %>% 
  ggplot(aes(cat_id, n, fill = cat_id)) +
  geom_col() +
  theme_hc() +
  theme(legend.position = "none") +
  theme(axis.text.x = element_text(size = 7)) +
  labs(x = "", y = "", title = "Rows per Category")

p3 <- extract_ts(bar) %>% 
  mutate(month = month(dates),
         year = year(dates)) %>% 
  group_by(month, year, id) %>% 
  summarise(sales = sum(sales),
            dates = min(dates)) %>% 
  ungroup() %>% 
  filter(str_detect(as.character(dates), "..-..-01")) %>% 
  filter(dates != max(dates)) %>% 
  mutate(state_id = str_sub(id, 1, 2)) %>% 
  ggplot(aes(dates, sales, col = id)) +
  geom_line() +
  theme_hc() +
  theme(legend.position = "bottom") +
  labs(title = "Sales per Store", x = "Date", y = "Sales", col = "Store ID") +
  facet_wrap(~state_id)

layout <- "
AAB
CCC
"

p1 + p2 + p3 + plot_layout(design = layout)

min_date <- date("2011-01-29")

foo <- train %>%
  group_by(dept_id, state_id) %>% 
  summarise_at(vars(starts_with("d_")), sum) %>% 
  ungroup() %>% 
  select(ends_with("id"), starts_with("d_")) %>%  
  pivot_longer(starts_with("d_"), names_to = "dates", values_to = "sales") %>%
  mutate(dates = as.integer(str_remove(dates, "d_"))) %>% 
  mutate(dates = min_date + dates - 1)

foo %>% 
  mutate(month = month(dates),
         year = year(dates)) %>% 
  group_by(month, year, dept_id, state_id) %>% 
  summarise(sales = sum(sales),
            dates = min(dates)) %>% 
  ungroup() %>% 
  filter(str_detect(as.character(dates), "..-..-01")) %>% 
  filter(dates != max(dates)) %>% 
  ggplot(aes(dates, sales, col = dept_id)) +
  geom_line() +
  facet_grid(state_id ~ dept_id) +
  theme_tufte() +
  theme(legend.position = "none", strip.text.x = element_text(size = 8)) +
  labs(title = "Sales per Department and State", x = "Date", y = "Sales")

foo <- train %>% 
  summarise_at(vars(starts_with("d_")), sum) %>% 
  mutate(id = 1)

bar <- extract_ts(foo) %>% 
  filter(!str_detect(as.character(dates), "-12-25"))

loess_all <- predict(loess(bar$sales ~ as.integer(bar$dates - min(bar$dates)) + 1, span = 1/2, degree = 1))

bar <- bar %>% 
  mutate(loess = loess_all) %>% 
  mutate(sales_rel = sales - loess)

p1 <- bar %>% 
  ggplot(aes(dates, sales)) +
  geom_line(col = "blue", alpha = 0.5) +
  geom_line(aes(dates, loess), col = "black") +
  theme_hc() +
  labs(x = "", y = "Sales", title = "Total Sales with Smoothing Fit + Seasonality in Residuals")

p2 <- bar %>% 
  mutate(wday = wday(dates, label = TRUE, week_start = 1),
         month = month(dates, label = TRUE),
         year = year(dates)) %>% 
  group_by(wday, month, year) %>% 
  summarise(sales = sum(sales_rel)/1e3) %>%
  ggplot(aes(month, wday, fill = sales)) +
  geom_tile() +
  labs(x = "Month of the year", y = "Day of the week", fill = "Relative Sales [1k]") +
  scale_fill_distiller(palette = "Spectral") +
  theme_hc()

p1 / p2

foo <- train %>%
  group_by(state_id) %>% 
  summarise_at(vars(starts_with("d_")), sum) %>% 
  rename(id = state_id)

bar <- extract_ts(foo) %>% 
  filter(!str_detect(as.character(dates), "-12-25")) %>% 
  group_by(id) %>% 
  mutate(loess = predict(loess(sales ~ as.integer(dates - min(dates)) + 1, span = 1/2, degree = 1)),
         mean_sales = (sales)) %>% 
  mutate(sales_rel = (sales - loess)/mean_sales)

p1 <- bar %>% 
  ggplot(aes(dates, sales, col = id)) +
  geom_line() +
  geom_line(aes(dates, loess), col = "black") +
  facet_wrap(~ id) +
  theme_tufte() +
  theme(legend.position = "none") +
  labs(x = "", y = "Sales", title = "Sales per State with Seasonalities")
Calts<-bar$mean_sales[1:1908]
WIts<-bar$mean_sales[1909:3816]
TXts<-bar$mean_sales[3817:5724]

CA.ts <- ts(Calts,start=1,freq=7)
WI.ts <- ts(WIts,start=1,freq=7)
TX.ts <- ts(TXts,start=1,freq=7)

{r} # library(dplyr) # wal <-read.csv(file='sales_train_validation.csv') # dt<-apply(wal, 1, function(r) any(r %in% c("CA"))) # Caldatachk<-as.matrix(wal[dt,7:1919]) # Calts<- colSums(Caldatachk) # dim(Calts) # Cal.ts <- ts(Calts,start=1,freq=7) #

{r} # dt2<-apply(wal, 1, function(r) any(r %in% c("TX"))) # sv<-as.matrix(wal[dt2,7:1919]) # TXdata<- colSums(sv) # TX.ts <- ts(TXdata,start=1,freq=7) # #

```{r}

dt3<-apply(wal, 1, function(r) any(r %in% c(“WI”)))

WIdata<- colSums(as.matrix(wal[dt3,7:1919]))

WI.ts <- ts(WIdata,start=1,freq=7)

plot(WI.ts,xlab=“Time (in days)”)

par(mfrow=c(1,3))
plot(CA.ts,xlab="Time (in days)")
plot(TX.ts,xlab="Time (in days)")
plot(WI.ts,xlab="Time (in days)")

The three time series for each of the states are represented as above, it clearly shows seasonality, with the christmas day have zero sales. We need to provide such extra bit of information(but how?)

totTs=CA.ts+TX.ts+WI.ts
plot(totTs,xlab="Time (in days)")

The combined time series shows heavy seasonality as well.

require(dlm)
Loading required package: dlm

Attaching package: 㤼㸱dlm㤼㸲

The following object is masked from 㤼㸱package:ggplot2㤼㸲:

    %+%
log.choc=log(totTs)
plot(log.choc)

# DLM with polynomial second-order trend and seasonality modeled with a seasonal factor representation.
build <- function(parm) {
  dlmModPoly(order = 2, dV = exp(parm[1]), dW = c(exp(parm[2]),exp(parm[3]))) + dlmModTrig(s = 7, dV = 0, dW=exp(parm[4]))
}
fit <- dlmMLE(log.choc, rep(0,4), build)
fit$convergence
[1] 0
BIC.2nd.seasfactor <-  2 *  fit$value + length(fit$par) * log(length(log.choc))
print("BIC")
[1] "BIC"
BIC.2nd.seasfactor
[1] -7175.684
model2 <- build(fit$par)  #This is part where he takes the model parameters
print("Observational noise from MLE")
[1] "Observational noise from MLE"
model2$V
            [,1]
[1,] 0.002868375
print("Innovation variance matrix diagonal elements from MLE")
[1] "Innovation variance matrix diagonal elements from MLE"
model2$W[1:2,1:2]
            [,1]         [,2]
[1,] 0.001297301 0.000000e+00
[2,] 0.000000000 2.916599e-14
chocfilt2 <- dlmFilter(log.choc, model2)

cov.filt <- with(chocfilt2, dlmSvd2var(U.C, D.C))

seas.term = 2

sd.seasonality.filt2 <- rep(NA,length(cov.filt))
for(i in 1:length(cov.filt)) sd.seasonality.filt2[i] = sqrt(cov.filt[[i]][seas.term,seas.term])
sd.seasonality.filt2 = ts(sd.seasonality.filt2[-(1:8)],frequency=7)
seasonality.filt2 = ts(chocfilt2$m[-(1:8),seas.term],frequency=7)
ll = seasonality.filt2 - 1.96 * sd.seasonality.filt2
ul = seasonality.filt2 + 1.96 * sd.seasonality.filt2
llim = min(ll)
ulim = max(ul)
plot(seasonality.filt2,ylim=c(llim,ulim))
lines(ll,lty=2,col="green")
lines(ul,lty=2,col="green")

llim = min(c(min(seasonality.filt2/sd.seasonality.filt2),-1.96))
ulim = max(c(max(seasonality.filt2/sd.seasonality.filt2),1.96))
plot(seasonality.filt2/sd.seasonality.filt2,ylim=c(llim,ulim))
abline(h=1.96,lty=2)
abline(h=-1.96,lty=2)

seas.term = 4

sd.seasonality.filt2 <- rep(NA,length(cov.filt))
for(i in 1:length(cov.filt)) sd.seasonality.filt2[i] = sqrt(cov.filt[[i]][seas.term,seas.term])
sd.seasonality.filt2 = ts(sd.seasonality.filt2[-(1:8)],frequency=7)
seasonality.filt2 = ts(chocfilt2$m[-(1:8),seas.term],frequency=7)
ll = seasonality.filt2 - 1.96 * sd.seasonality.filt2
ul = seasonality.filt2 + 1.96 * sd.seasonality.filt2
llim = min(ll)
ulim = max(ul)
plot(seasonality.filt2,ylim=c(llim,ulim))
lines(ll,lty=2,col="green")
lines(ul,lty=2,col="green")

llim = min(c(min(seasonality.filt2/sd.seasonality.filt2),-1.96))
ulim = max(c(max(seasonality.filt2/sd.seasonality.filt2),1.96))
plot(seasonality.filt2/sd.seasonality.filt2,ylim=c(llim,ulim))
abline(h=1.96,lty=2)
abline(h=-1.96,lty=2)




seas.term = 6

sd.seasonality.filt2 <- rep(NA,length(cov.filt))
for(i in 1:length(cov.filt)) sd.seasonality.filt2[i] = sqrt(cov.filt[[i]][seas.term,seas.term])
sd.seasonality.filt2 = ts(sd.seasonality.filt2[-(1:8)],frequency=7)
seasonality.filt2 = ts(chocfilt2$m[-(1:8),seas.term],frequency=7)
ll = seasonality.filt2 - 1.96 * sd.seasonality.filt2
ul = seasonality.filt2 + 1.96 * sd.seasonality.filt2
llim = min(ll)
ulim = max(ul)
plot(seasonality.filt2,ylim=c(llim,ulim))
lines(ll,lty=2,col="green")
lines(ul,lty=2,col="green")

llim = min(c(min(seasonality.filt2/sd.seasonality.filt2),-1.96))
ulim = max(c(max(seasonality.filt2/sd.seasonality.filt2),1.96))
plot(seasonality.filt2/sd.seasonality.filt2,ylim=c(llim,ulim))
abline(h=1.96,lty=2)
abline(h=-1.96,lty=2)



seas.term = 8

sd.seasonality.filt2 <- rep(NA,length(cov.filt))
for(i in 1:length(cov.filt)) sd.seasonality.filt2[i] = sqrt(cov.filt[[i]][seas.term,seas.term])
sd.seasonality.filt2 = ts(sd.seasonality.filt2[-(1:8)],frequency=7)
seasonality.filt2 = ts(chocfilt2$m[-(1:8),seas.term],frequency=7)
ll = seasonality.filt2 - 1.96 * sd.seasonality.filt2
ul = seasonality.filt2 + 1.96 * sd.seasonality.filt2
llim = min(ll)
ulim = max(ul)
plot(seasonality.filt2,ylim=c(llim,ulim))
lines(ll,lty=2,col="green")
lines(ul,lty=2,col="green")

llim = min(c(min(seasonality.filt2/sd.seasonality.filt2),-1.96))
ulim = max(c(max(seasonality.filt2/sd.seasonality.filt2),1.96))
plot(seasonality.filt2/sd.seasonality.filt2,ylim=c(llim,ulim))
abline(h=1.96,lty=2)
abline(h=-1.96,lty=2)






###################
#   Forecasting   #
###################

predictions <- dlmForecast(chocfilt2, n=28)

ll = predictions$f - 1.96 * sqrt(unlist(predictions$Q))
ul = predictions$f + 1.96 * sqrt(unlist(predictions$Q))
plot(log.choc, xlab = "", col = "darkgrey",xlim=c(250,300),lwd=2)
#plot(log.choc, xlab = "", col = "darkgrey",xlim=c(1958,2000), ylim=c(1000,10000),lwd=2)
lines(predictions$f, col="red",lwd=2)
lines(ll,lty=2, col="green",lwd=2)
lines(ul,lty=2, col="green",lwd=2)


######################################################
#          One-step ahead forecast error for the last 9 years                #
######################################################
print(" Mean absolute forecast error")
[1] " Mean absolute forecast error"
# Mean absolute forecast error (MAE)
mean(abs(chocfilt2$f[1800:1908] - log.choc[1800:1908]))
[1] 0.06380559
print(" Mean squared forecast error (MSE)")
[1] " Mean squared forecast error (MSE)"
# Mean squared forecast error (MSE)
mean((chocfilt2$f[1800:1908] - log.choc[1800:1908])^2)
[1] 0.006618175
print(" Mean absolute percentage forecast error (MAPE)")
[1] " Mean absolute percentage forecast error (MAPE)"
# Mean absolute percentage forecast error (MAPE)
mean(abs(chocfilt2$f[1800:1908] - log.choc[1800:1908]) / log.choc[1800:1908])
[1] 0.005999049
plot(chocfilt2$f,ylim=c(10,11))
lines(log.choc,col="green")

```

LS0tDQp0aXRsZTogIlIgTm90ZWJvb2siDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQpUaGlzIGlzIGFuIFtSIE1hcmtkb3duXShodHRwOi8vcm1hcmtkb3duLnJzdHVkaW8uY29tKSBOb3RlYm9vay4gV2hlbiB5b3UgZXhlY3V0ZSBjb2RlIHdpdGhpbiB0aGUgbm90ZWJvb2ssIHRoZSByZXN1bHRzIGFwcGVhciBiZW5lYXRoIHRoZSBjb2RlLiANCg0KVHJ5IGV4ZWN1dGluZyB0aGlzIGNodW5rIGJ5IGNsaWNraW5nIHRoZSAqUnVuKiBidXR0b24gd2l0aGluIHRoZSBjaHVuayBvciBieSBwbGFjaW5nIHlvdXIgY3Vyc29yIGluc2lkZSBpdCBhbmQgcHJlc3NpbmcgKkN0cmwrU2hpZnQrRW50ZXIqLiANCmBgYHtyfQ0KIyBnZW5lcmFsIHZpc3VhbGlzYXRpb24NCmxpYnJhcnkoJ2dncGxvdDInKSAjIHZpc3VhbGlzYXRpb24NCmxpYnJhcnkoJ3NjYWxlcycpICMgdmlzdWFsaXNhdGlvbg0KbGlicmFyeSgncGF0Y2h3b3JrJykgIyB2aXN1YWxpc2F0aW9uDQpsaWJyYXJ5KCdSQ29sb3JCcmV3ZXInKSAjIHZpc3VhbGlzYXRpb24NCmxpYnJhcnkoJ2NvcnJwbG90JykgIyB2aXN1YWxpc2F0aW9uDQoNCiMgZ2VuZXJhbCBkYXRhIG1hbmlwdWxhdGlvbg0KbGlicmFyeSgnZHBseXInKSAjIGRhdGEgbWFuaXB1bGF0aW9uDQpsaWJyYXJ5KCdyZWFkcicpICMgaW5wdXQvb3V0cHV0DQpsaWJyYXJ5KCd2cm9vbScpICMgaW5wdXQvb3V0cHV0DQpsaWJyYXJ5KCdza2ltcicpICMgb3ZlcnZpZXcNCmxpYnJhcnkoJ3RpYmJsZScpICMgZGF0YSB3cmFuZ2xpbmcNCmxpYnJhcnkoJ3RpZHlyJykgIyBkYXRhIHdyYW5nbGluZw0KbGlicmFyeSgncHVycnInKSAjIGRhdGEgd3JhbmdsaW5nDQpsaWJyYXJ5KCdzdHJpbmdyJykgIyBzdHJpbmcgbWFuaXB1bGF0aW9uDQpsaWJyYXJ5KCdmb3JjYXRzJykgIyBmYWN0b3IgbWFuaXB1bGF0aW9uDQoNCiMgc3BlY2lmaWMgdmlzdWFsaXNhdGlvbg0KbGlicmFyeSgnYWxsdXZpYWwnKSAjIHZpc3VhbGlzYXRpb24NCmxpYnJhcnkoJ2dncmVwZWwnKSAjIHZpc3VhbGlzYXRpb24NCmxpYnJhcnkoJ2dnZm9yY2UnKSAjIHZpc3VhbGlzYXRpb24NCmxpYnJhcnkoJ2dncmlkZ2VzJykgIyB2aXN1YWxpc2F0aW9uDQpsaWJyYXJ5KCdnZ2FuaW1hdGUnKSAjIGFuaW1hdGlvbnMNCmxpYnJhcnkoJ0dHYWxseScpICMgdmlzdWFsaXNhdGlvbg0KbGlicmFyeSgnZ2d0aGVtZXMnKSAjIHZpc3VhbGlzYXRpb24NCmxpYnJhcnkoJ3dlc2FuZGVyc29uJykgIyB2aXN1YWxpc2F0aW9uDQpsaWJyYXJ5KCdrYWJsZUV4dHJhJykgIyBkaXNwbGF5DQoNCiMgRGF0ZSArIGZvcmVjYXN0DQpsaWJyYXJ5KCdsdWJyaWRhdGUnKSAjIGRhdGUgYW5kIHRpbWUNCmxpYnJhcnkoJ2ZvcmVjYXN0JykgIyB0aW1lIHNlcmllcyBhbmFseXNpcw0KI2xpYnJhcnkoJ3Byb3BoZXQnKSAjIHRpbWUgc2VyaWVzIGFuYWx5c2lzDQpsaWJyYXJ5KCd0aW1ldGsnKSAjIHRpbWUgc2VyaWVzIGFuYWx5c2lzDQoNCiMgSW50ZXJhY3Rpdml0eQ0KbGlicmFyeSgnY3Jvc3N0YWxrJykNCmxpYnJhcnkoJ3Bsb3RseScpDQoNCiMgcGFyYWxsZWwNCmxpYnJhcnkoJ2ZvcmVhY2gnKQ0KbGlicmFyeSgnZG9QYXJhbGxlbCcpDQpgYGANCg0KYGBge3J9DQpsaWJyYXJ5KHZyb29tKQ0KbGlicmFyeShzdHJpbmdyKQ0KbGlicmFyeSh0aWR5dmVyc2UpDQp0cmFpbiA8LSB2cm9vbShzdHJfYygnc2FsZXNfdHJhaW5fdmFsaWRhdGlvbi5jc3YnKSwgZGVsaW0gPSAiLCIsIGNvbF90eXBlcyA9IGNvbHMoKSkNCnByaWNlcyA8LSB2cm9vbShzdHJfYygnc2VsbF9wcmljZXMuY3N2JyksIGRlbGltID0gIiwiLCBjb2xfdHlwZXMgPSBjb2xzKCkpDQpjYWxlbmRhciA8LSByZWFkX2NzdihzdHJfYygnY2FsZW5kYXIuY3N2JyksIGNvbF90eXBlcyA9IGNvbHMoKSkNCg0Kc2FtcGxlX3N1Ym1pdCA8LSB2cm9vbShzdHJfYygnc2FtcGxlX3N1Ym1pc3Npb24uY3N2JyksIGRlbGltID0gIiwiLCBjb2xfdHlwZXMgPSBjb2xzKCkpDQpgYGANCg0KQWRkIGEgbmV3IGNodW5rIGJ5IGNsaWNraW5nIHRoZSAqSW5zZXJ0IENodW5rKiBidXR0b24gb24gdGhlIHRvb2xiYXIgb3IgYnkgcHJlc3NpbmcgKkN0cmwrQWx0K0kqLg0KDQpXaGVuIHlvdSBzYXZlIHRoZSBub3RlYm9vaywgYW4gSFRNTCBmaWxlIGNvbnRhaW5pbmcgdGhlIGNvZGUgYW5kIG91dHB1dCB3aWxsIGJlIHNhdmVkIGFsb25nc2lkZSBpdCAoY2xpY2sgdGhlICpQcmV2aWV3KiBidXR0b24gb3IgcHJlc3MgKkN0cmwrU2hpZnQrSyogdG8gcHJldmlldyB0aGUgSFRNTCBmaWxlKS4NCg0KVGhlIHByZXZpZXcgc2hvd3MgeW91IGEgcmVuZGVyZWQgSFRNTCBjb3B5IG9mIHRoZSBjb250ZW50cyBvZiB0aGUgZWRpdG9yLiBDb25zZXF1ZW50bHksIHVubGlrZSAqS25pdCosICpQcmV2aWV3KiBkb2VzIG5vdCBydW4gYW55IFIgY29kZSBjaHVua3MuIEluc3RlYWQsIHRoZSBvdXRwdXQgb2YgdGhlIGNodW5rIHdoZW4gaXQgd2FzIGxhc3QgcnVuIGluIHRoZSBlZGl0b3IgaXMgZGlzcGxheWVkLg0KYGBge3J9DQpleHRyYWN0X3RzIDwtIGZ1bmN0aW9uKGRmKXsNCiAgDQogIG1pbl9kYXRlIDwtIGFzLkRhdGUoIjIwMTEtMDEtMjkiKQ0KICANCiAgZGYgJT4lDQogICAgc2VsZWN0KGlkLCBzdGFydHNfd2l0aCgiZF8iKSkgJT4lICANCiAgICBwaXZvdF9sb25nZXIoc3RhcnRzX3dpdGgoImRfIiksIG5hbWVzX3RvID0gImRhdGVzIiwgdmFsdWVzX3RvID0gInNhbGVzIikgJT4lDQogICAgbXV0YXRlKGRhdGVzID0gYXMuaW50ZWdlcihzdHJfcmVtb3ZlKGRhdGVzLCAiZF8iKSkpICU+JSANCiAgICBtdXRhdGUoZGF0ZXMgPSBtaW5fZGF0ZSArIGRhdGVzIC0gMSkgJT4lIA0KICAgIG11dGF0ZShpZCA9IHN0cl9yZW1vdmUoaWQsICJfdmFsaWRhdGlvbiIpKQ0KfQ0KDQpzZXQuc2VlZCg0MzIxKQ0KZm9vIDwtIHRyYWluICU+JSANCiAgc2FtcGxlX24oNTApDQoNCnRzX291dCA8LSBleHRyYWN0X3RzKGZvbykNCg0KY29scyA8LSB0c19vdXQgJT4lIA0KICBkaXN0aW5jdChpZCkgJT4lIA0KICBtdXRhdGUoY29scyA9IHJlcF9sZW4oYnJld2VyLnBhbCg3LCAiU2V0MiIpLCBsZW5ndGgub3V0ID0gbl9kaXN0aW5jdCh0c19vdXQkaWQpKSkNCg0KdHNfb3V0IDwtIHRzX291dCAlPiUgDQogIGxlZnRfam9pbihjb2xzLCBieSA9ICJpZCIpDQoNCnBhbCA8LSBjb2xzJGNvbHMgJT4lDQogICBzZXROYW1lcyhjb2xzJGlkKQ0KYGBgDQoNCmBgYHtyfQ0Kc2hhcmVkX3RzIDwtIGhpZ2hsaWdodF9rZXkodHNfb3V0KQ0KDQpwYWxldHRlKGJyZXdlci5wYWwoOSwgIlNldDMiKSkNCg0KZ2cgPC0gc2hhcmVkX3RzICU+JSANCiAgZ2dwbG90KGFlcyhkYXRlcywgc2FsZXMsIGNvbCA9IGlkLCBncm91cCA9IGlkKSkgKw0KICBnZW9tX2xpbmUoKSArDQogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBwYWwpICsNCiAgbGFicyh4ID0gIkRhdGUiLCB5ID0gIlNhbGVzIikgKw0KICB0aGVtZV90dWZ0ZSgpICsNCiAgTlVMTA0KDQpmaWx0ZXIgPC0gYnNjb2xzKA0KICBmaWx0ZXJfc2VsZWN0KCJpZHMiLCAiU2FsZXMgb3ZlciB0aW1lOiBTZWxlY3QgYSB0aW1lIHNlcmllcyBJRCAocmVtb3ZlIHdpdGggYmFja3NwYWNlIGtleSwgbmF2aWdhdGUgd2l0aCBhcnJvdyBrZXlzKToiLCBzaGFyZWRfdHMsIH5pZCwgbXVsdGlwbGUgPSBUUlVFKSwNCiAgZ2dwbG90bHkoZ2csIGR5bmFtaWNUaWNrcyA9IFRSVUUpLA0KICB3aWR0aHMgPSBjKDEyLCAxMikNCikNCg0KYnNjb2xzKGZpbHRlcikNCmBgYA0KYGBge3J9DQpmb28gPC0gdHJhaW4gJT4lIA0KICBzdW1tYXJpc2VfYXQodmFycyhzdGFydHNfd2l0aCgiZF8iKSksIHN1bSkgJT4lIA0KICBtdXRhdGUoaWQgPSAxKQ0KDQpiYXIgPC0gZXh0cmFjdF90cyhmb28pDQoNCmdnIDwtIGJhciAlPiUgDQogIGdncGxvdChhZXMoZGF0ZXMsIHNhbGVzKSkgKw0KICBnZW9tX2xpbmUoY29sID0gImJsdWUiKSArDQogIHRoZW1lX3R1ZnRlKCkgKw0KICBsYWJzKHggPSAiRGF0ZSIsIHkgPSAiU2FsZXMiLCB0aXRsZSA9ICJBbGwgYWdncmVnYXRlIHNhbGVzIikNCg0KZ2dwbG90bHkoZ2csIGR5bmFtaWNUaWNrcyA9IFRSVUUpDQpgYGANCmBgYHtyfQ0KZm9vIDwtIHRyYWluICU+JSANCiAgc3VtbWFyaXNlX2F0KHZhcnMoc3RhcnRzX3dpdGgoImRfIikpLCBzdW0pICU+JSANCiAgbXV0YXRlKGlkID0gMSkNCg0KYmFyIDwtIGV4dHJhY3RfdHMoZm9vKQ0KDQpnZyA8LSBiYXIgJT4lIA0KICBnZ3Bsb3QoYWVzKGRhdGVzLCBzYWxlcykpICsNCiAgZ2VvbV9saW5lKGNvbCA9ICJibHVlIikgKw0KICB0aGVtZV90dWZ0ZSgpICsNCiAgbGFicyh4ID0gIkRhdGUiLCB5ID0gIlNhbGVzIiwgdGl0bGUgPSAiQWxsIGFnZ3JlZ2F0ZSBzYWxlcyIpDQoNCmdncGxvdGx5KGdnLCBkeW5hbWljVGlja3MgPSBUUlVFKQ0KYGBgDQpgYGB7cn0NCmZvbyA8LSB0cmFpbiAlPiUNCiAgZ3JvdXBfYnkoc3RhdGVfaWQpICU+JSANCiAgc3VtbWFyaXNlX2F0KHZhcnMoc3RhcnRzX3dpdGgoImRfIikpLCBzdW0pICU+JSANCiAgcmVuYW1lKGlkID0gc3RhdGVfaWQpDQoNCmJhciA8LSBleHRyYWN0X3RzKGZvbykgJT4lIA0KICBtdXRhdGUobW9udGggPSBtb250aChkYXRlcyksDQogICAgICAgICB5ZWFyID0geWVhcihkYXRlcykpICU+JSANCiAgZ3JvdXBfYnkobW9udGgsIHllYXIsIGlkKSAlPiUgDQogIHN1bW1hcmlzZShzYWxlcyA9IHN1bShzYWxlcyksDQogICAgICAgICAgICBkYXRlcyA9IG1pbihkYXRlcykpICU+JSANCiAgdW5ncm91cCgpICU+JSANCiAgZmlsdGVyKHN0cl9kZXRlY3QoYXMuY2hhcmFjdGVyKGRhdGVzKSwgIi4uLS4uLTAxIikpICU+JSANCiAgZmlsdGVyKGRhdGVzICE9IG1heChkYXRlcykpDQoNCmdnIDwtIGJhciAlPiUgDQogIGdncGxvdChhZXMoZGF0ZXMsIHNhbGVzLCBjb2wgPSBpZCkpICsNCiAgZ2VvbV9saW5lKCkgKw0KICB0aGVtZV90dWZ0ZSgpICsNCiAgbGFicyh4ID0gIkRhdGUiLCB5ID0gIlNhbGVzIiwgdGl0bGUgPSAiTW9udGhseSBTYWxlcyBwZXIgU3RhdGUiKQ0KDQpnZ3Bsb3RseShnZywgZHluYW1pY1RpY2tzID0gVFJVRSkNCmBgYA0KDQoNCg0KDQoNCmBgYHtyfQ0KZm9vIDwtIHRyYWluICU+JQ0KICBncm91cF9ieShjYXRfaWQpICU+JSANCiAgc3VtbWFyaXNlX2F0KHZhcnMoc3RhcnRzX3dpdGgoImRfIikpLCBzdW0pICU+JSANCiAgcmVuYW1lKGlkID0gY2F0X2lkKQ0KDQpiYXIgPC0gdHJhaW4gJT4lDQogIGdyb3VwX2J5KHN0b3JlX2lkKSAlPiUgDQogIHN1bW1hcmlzZV9hdCh2YXJzKHN0YXJ0c193aXRoKCJkXyIpKSwgc3VtKSAlPiUgDQogIHJlbmFtZShpZCA9IHN0b3JlX2lkKQ0KDQpwMSA8LSBleHRyYWN0X3RzKGZvbykgJT4lIA0KICBtdXRhdGUobW9udGggPSBtb250aChkYXRlcyksDQogICAgICAgICB5ZWFyID0geWVhcihkYXRlcykpICU+JSANCiAgZ3JvdXBfYnkobW9udGgsIHllYXIsIGlkKSAlPiUgDQogIHN1bW1hcmlzZShzYWxlcyA9IHN1bShzYWxlcyksDQogICAgICAgICAgICBkYXRlcyA9IG1pbihkYXRlcykpICU+JSANCiAgdW5ncm91cCgpICU+JSANCiAgZmlsdGVyKHN0cl9kZXRlY3QoYXMuY2hhcmFjdGVyKGRhdGVzKSwgIi4uLS4uLTAxIikpICU+JSANCiAgZmlsdGVyKGRhdGVzICE9IG1heChkYXRlcykpICU+JSANCiAgZ2dwbG90KGFlcyhkYXRlcywgc2FsZXMsIGNvbCA9IGlkKSkgKw0KICBnZW9tX2xpbmUoKSArDQogIHRoZW1lX2hjKCkgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpICsNCiAgbGFicyh0aXRsZSA9ICJTYWxlcyBwZXIgQ2F0ZWdvcnkiLCB4ID0gIkRhdGUiLCB5ID0gIlNhbGVzIikNCg0KcDIgPC0gdHJhaW4gJT4lIA0KICBjb3VudChjYXRfaWQpICU+JSANCiAgZ2dwbG90KGFlcyhjYXRfaWQsIG4sIGZpbGwgPSBjYXRfaWQpKSArDQogIGdlb21fY29sKCkgKw0KICB0aGVtZV9oYygpICsNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArDQogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSA3KSkgKw0KICBsYWJzKHggPSAiIiwgeSA9ICIiLCB0aXRsZSA9ICJSb3dzIHBlciBDYXRlZ29yeSIpDQoNCnAzIDwtIGV4dHJhY3RfdHMoYmFyKSAlPiUgDQogIG11dGF0ZShtb250aCA9IG1vbnRoKGRhdGVzKSwNCiAgICAgICAgIHllYXIgPSB5ZWFyKGRhdGVzKSkgJT4lIA0KICBncm91cF9ieShtb250aCwgeWVhciwgaWQpICU+JSANCiAgc3VtbWFyaXNlKHNhbGVzID0gc3VtKHNhbGVzKSwNCiAgICAgICAgICAgIGRhdGVzID0gbWluKGRhdGVzKSkgJT4lIA0KICB1bmdyb3VwKCkgJT4lIA0KICBmaWx0ZXIoc3RyX2RldGVjdChhcy5jaGFyYWN0ZXIoZGF0ZXMpLCAiLi4tLi4tMDEiKSkgJT4lIA0KICBmaWx0ZXIoZGF0ZXMgIT0gbWF4KGRhdGVzKSkgJT4lIA0KICBtdXRhdGUoc3RhdGVfaWQgPSBzdHJfc3ViKGlkLCAxLCAyKSkgJT4lIA0KICBnZ3Bsb3QoYWVzKGRhdGVzLCBzYWxlcywgY29sID0gaWQpKSArDQogIGdlb21fbGluZSgpICsNCiAgdGhlbWVfaGMoKSArDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKSArDQogIGxhYnModGl0bGUgPSAiU2FsZXMgcGVyIFN0b3JlIiwgeCA9ICJEYXRlIiwgeSA9ICJTYWxlcyIsIGNvbCA9ICJTdG9yZSBJRCIpICsNCiAgZmFjZXRfd3JhcCh+c3RhdGVfaWQpDQoNCmxheW91dCA8LSAiDQpBQUINCkNDQw0KIg0KDQpwMSArIHAyICsgcDMgKyBwbG90X2xheW91dChkZXNpZ24gPSBsYXlvdXQpDQpgYGANCg0KYGBge3J9DQptaW5fZGF0ZSA8LSBkYXRlKCIyMDExLTAxLTI5IikNCg0KZm9vIDwtIHRyYWluICU+JQ0KICBncm91cF9ieShkZXB0X2lkLCBzdGF0ZV9pZCkgJT4lIA0KICBzdW1tYXJpc2VfYXQodmFycyhzdGFydHNfd2l0aCgiZF8iKSksIHN1bSkgJT4lIA0KICB1bmdyb3VwKCkgJT4lIA0KICBzZWxlY3QoZW5kc193aXRoKCJpZCIpLCBzdGFydHNfd2l0aCgiZF8iKSkgJT4lICANCiAgcGl2b3RfbG9uZ2VyKHN0YXJ0c193aXRoKCJkXyIpLCBuYW1lc190byA9ICJkYXRlcyIsIHZhbHVlc190byA9ICJzYWxlcyIpICU+JQ0KICBtdXRhdGUoZGF0ZXMgPSBhcy5pbnRlZ2VyKHN0cl9yZW1vdmUoZGF0ZXMsICJkXyIpKSkgJT4lIA0KICBtdXRhdGUoZGF0ZXMgPSBtaW5fZGF0ZSArIGRhdGVzIC0gMSkNCg0KZm9vICU+JSANCiAgbXV0YXRlKG1vbnRoID0gbW9udGgoZGF0ZXMpLA0KICAgICAgICAgeWVhciA9IHllYXIoZGF0ZXMpKSAlPiUgDQogIGdyb3VwX2J5KG1vbnRoLCB5ZWFyLCBkZXB0X2lkLCBzdGF0ZV9pZCkgJT4lIA0KICBzdW1tYXJpc2Uoc2FsZXMgPSBzdW0oc2FsZXMpLA0KICAgICAgICAgICAgZGF0ZXMgPSBtaW4oZGF0ZXMpKSAlPiUgDQogIHVuZ3JvdXAoKSAlPiUgDQogIGZpbHRlcihzdHJfZGV0ZWN0KGFzLmNoYXJhY3RlcihkYXRlcyksICIuLi0uLi0wMSIpKSAlPiUgDQogIGZpbHRlcihkYXRlcyAhPSBtYXgoZGF0ZXMpKSAlPiUgDQogIGdncGxvdChhZXMoZGF0ZXMsIHNhbGVzLCBjb2wgPSBkZXB0X2lkKSkgKw0KICBnZW9tX2xpbmUoKSArDQogIGZhY2V0X2dyaWQoc3RhdGVfaWQgfiBkZXB0X2lkKSArDQogIHRoZW1lX3R1ZnRlKCkgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIsIHN0cmlwLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gOCkpICsNCiAgbGFicyh0aXRsZSA9ICJTYWxlcyBwZXIgRGVwYXJ0bWVudCBhbmQgU3RhdGUiLCB4ID0gIkRhdGUiLCB5ID0gIlNhbGVzIikNCmBgYA0KYGBge3J9DQpmb28gPC0gdHJhaW4gJT4lIA0KICBzdW1tYXJpc2VfYXQodmFycyhzdGFydHNfd2l0aCgiZF8iKSksIHN1bSkgJT4lIA0KICBtdXRhdGUoaWQgPSAxKQ0KDQpiYXIgPC0gZXh0cmFjdF90cyhmb28pICU+JSANCiAgZmlsdGVyKCFzdHJfZGV0ZWN0KGFzLmNoYXJhY3RlcihkYXRlcyksICItMTItMjUiKSkNCg0KbG9lc3NfYWxsIDwtIHByZWRpY3QobG9lc3MoYmFyJHNhbGVzIH4gYXMuaW50ZWdlcihiYXIkZGF0ZXMgLSBtaW4oYmFyJGRhdGVzKSkgKyAxLCBzcGFuID0gMS8yLCBkZWdyZWUgPSAxKSkNCg0KYmFyIDwtIGJhciAlPiUgDQogIG11dGF0ZShsb2VzcyA9IGxvZXNzX2FsbCkgJT4lIA0KICBtdXRhdGUoc2FsZXNfcmVsID0gc2FsZXMgLSBsb2VzcykNCg0KcDEgPC0gYmFyICU+JSANCiAgZ2dwbG90KGFlcyhkYXRlcywgc2FsZXMpKSArDQogIGdlb21fbGluZShjb2wgPSAiYmx1ZSIsIGFscGhhID0gMC41KSArDQogIGdlb21fbGluZShhZXMoZGF0ZXMsIGxvZXNzKSwgY29sID0gImJsYWNrIikgKw0KICB0aGVtZV9oYygpICsNCiAgbGFicyh4ID0gIiIsIHkgPSAiU2FsZXMiLCB0aXRsZSA9ICJUb3RhbCBTYWxlcyB3aXRoIFNtb290aGluZyBGaXQgKyBTZWFzb25hbGl0eSBpbiBSZXNpZHVhbHMiKQ0KDQpwMiA8LSBiYXIgJT4lIA0KICBtdXRhdGUod2RheSA9IHdkYXkoZGF0ZXMsIGxhYmVsID0gVFJVRSwgd2Vla19zdGFydCA9IDEpLA0KICAgICAgICAgbW9udGggPSBtb250aChkYXRlcywgbGFiZWwgPSBUUlVFKSwNCiAgICAgICAgIHllYXIgPSB5ZWFyKGRhdGVzKSkgJT4lIA0KICBncm91cF9ieSh3ZGF5LCBtb250aCwgeWVhcikgJT4lIA0KICBzdW1tYXJpc2Uoc2FsZXMgPSBzdW0oc2FsZXNfcmVsKS8xZTMpICU+JQ0KICBnZ3Bsb3QoYWVzKG1vbnRoLCB3ZGF5LCBmaWxsID0gc2FsZXMpKSArDQogIGdlb21fdGlsZSgpICsNCiAgbGFicyh4ID0gIk1vbnRoIG9mIHRoZSB5ZWFyIiwgeSA9ICJEYXkgb2YgdGhlIHdlZWsiLCBmaWxsID0gIlJlbGF0aXZlIFNhbGVzIFsxa10iKSArDQogIHNjYWxlX2ZpbGxfZGlzdGlsbGVyKHBhbGV0dGUgPSAiU3BlY3RyYWwiKSArDQogIHRoZW1lX2hjKCkNCg0KcDEgLyBwMg0KYGBgDQpgYGB7cn0NCmZvbyA8LSB0cmFpbiAlPiUNCiAgZ3JvdXBfYnkoc3RhdGVfaWQpICU+JSANCiAgc3VtbWFyaXNlX2F0KHZhcnMoc3RhcnRzX3dpdGgoImRfIikpLCBzdW0pICU+JSANCiAgcmVuYW1lKGlkID0gc3RhdGVfaWQpDQoNCmJhciA8LSBleHRyYWN0X3RzKGZvbykgJT4lIA0KICBmaWx0ZXIoIXN0cl9kZXRlY3QoYXMuY2hhcmFjdGVyKGRhdGVzKSwgIi0xMi0yNSIpKSAlPiUgDQogIGdyb3VwX2J5KGlkKSAlPiUgDQogIG11dGF0ZShsb2VzcyA9IHByZWRpY3QobG9lc3Moc2FsZXMgfiBhcy5pbnRlZ2VyKGRhdGVzIC0gbWluKGRhdGVzKSkgKyAxLCBzcGFuID0gMS8yLCBkZWdyZWUgPSAxKSksDQogICAgICAgICBtZWFuX3NhbGVzID0gKHNhbGVzKSkgJT4lIA0KICBtdXRhdGUoc2FsZXNfcmVsID0gKHNhbGVzIC0gbG9lc3MpL21lYW5fc2FsZXMpDQoNCnAxIDwtIGJhciAlPiUgDQogIGdncGxvdChhZXMoZGF0ZXMsIHNhbGVzLCBjb2wgPSBpZCkpICsNCiAgZ2VvbV9saW5lKCkgKw0KICBnZW9tX2xpbmUoYWVzKGRhdGVzLCBsb2VzcyksIGNvbCA9ICJibGFjayIpICsNCiAgZmFjZXRfd3JhcCh+IGlkKSArDQogIHRoZW1lX3R1ZnRlKCkgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpICsNCiAgbGFicyh4ID0gIiIsIHkgPSAiU2FsZXMiLCB0aXRsZSA9ICJTYWxlcyBwZXIgU3RhdGUgd2l0aCBTZWFzb25hbGl0aWVzIikNCg0KYGBgDQoNCmBgYHtyfQ0KQ2FsdHM8LWJhciRtZWFuX3NhbGVzWzE6MTkwOF0NCldJdHM8LWJhciRtZWFuX3NhbGVzWzE5MDk6MzgxNl0NClRYdHM8LWJhciRtZWFuX3NhbGVzWzM4MTc6NTcyNF0NCg0KQ0EudHMgPC0gdHMoQ2FsdHMsc3RhcnQ9MSxmcmVxPTcpDQpXSS50cyA8LSB0cyhXSXRzLHN0YXJ0PTEsZnJlcT03KQ0KVFgudHMgPC0gdHMoVFh0cyxzdGFydD0xLGZyZXE9NykNCmBgYA0KDQoNCg0KIyBgYGB7cn0NCiMgbGlicmFyeShkcGx5cikNCiMgd2FsIDwtcmVhZC5jc3YoZmlsZT0nc2FsZXNfdHJhaW5fdmFsaWRhdGlvbi5jc3YnKQ0KIyBkdDwtYXBwbHkod2FsLCAxLCBmdW5jdGlvbihyKSBhbnkociAlaW4lIGMoIkNBIikpKQ0KIyBDYWxkYXRhY2hrPC1hcy5tYXRyaXgod2FsW2R0LDc6MTkxOV0pDQojIENhbHRzPC0gY29sU3VtcyhDYWxkYXRhY2hrKQ0KIyBkaW0oQ2FsdHMpDQojIENhbC50cyA8LSB0cyhDYWx0cyxzdGFydD0xLGZyZXE9NykNCiMgYGBgDQojIA0KIyANCiMgYGBge3J9DQojIGR0MjwtYXBwbHkod2FsLCAxLCBmdW5jdGlvbihyKSBhbnkociAlaW4lIGMoIlRYIikpKQ0KIyBzdjwtYXMubWF0cml4KHdhbFtkdDIsNzoxOTE5XSkNCiMgVFhkYXRhPC0gY29sU3VtcyhzdikNCiMgVFgudHMgPC0gdHMoVFhkYXRhLHN0YXJ0PTEsZnJlcT03KQ0KIyANCiMgYGBgDQojIA0KIyANCiMgYGBge3J9DQojIGR0MzwtYXBwbHkod2FsLCAxLCBmdW5jdGlvbihyKSBhbnkociAlaW4lIGMoIldJIikpKQ0KIyBXSWRhdGE8LSBjb2xTdW1zKGFzLm1hdHJpeCh3YWxbZHQzLDc6MTkxOV0pKQ0KIyBXSS50cyA8LSB0cyhXSWRhdGEsc3RhcnQ9MSxmcmVxPTcpDQojIHBsb3QoV0kudHMseGxhYj0iVGltZSAoaW4gZGF5cykiKQ0KDQoNCmBgYHtyfQ0KcGFyKG1mcm93PWMoMSwzKSkNCnBsb3QoQ0EudHMseGxhYj0iVGltZSAoaW4gZGF5cykiKQ0KcGxvdChUWC50cyx4bGFiPSJUaW1lIChpbiBkYXlzKSIpDQpwbG90KFdJLnRzLHhsYWI9IlRpbWUgKGluIGRheXMpIikNCmBgYA0KVGhlIHRocmVlIHRpbWUgc2VyaWVzIGZvciBlYWNoIG9mIHRoZSBzdGF0ZXMgYXJlIHJlcHJlc2VudGVkIGFzIGFib3ZlLCBpdCBjbGVhcmx5IHNob3dzIHNlYXNvbmFsaXR5LCB3aXRoIHRoZSBjaHJpc3RtYXMgZGF5IGhhdmUgemVybyBzYWxlcy4gV2UgbmVlZCB0byBwcm92aWRlIHN1Y2ggZXh0cmEgYml0IG9mIGluZm9ybWF0aW9uKGJ1dCBob3c/KQ0KYGBge3J9DQp0b3RUcz1DQS50cytUWC50cytXSS50cw0KcGxvdCh0b3RUcyx4bGFiPSJUaW1lIChpbiBkYXlzKSIpDQpgYGANClRoZSBjb21iaW5lZCB0aW1lIHNlcmllcyBzaG93cyBoZWF2eSBzZWFzb25hbGl0eSBhcyB3ZWxsLiANCmBgYHtyfQ0KcmVxdWlyZShkbG0pDQoNCmxvZy5jaG9jPWxvZyh0b3RUcykNCnBsb3QobG9nLmNob2MpDQpgYGANCg0KYGBge3J9DQojIERMTSB3aXRoIHBvbHlub21pYWwgc2Vjb25kLW9yZGVyIHRyZW5kIGFuZCBzZWFzb25hbGl0eSBtb2RlbGVkIHdpdGggYSBzZWFzb25hbCBmYWN0b3IgcmVwcmVzZW50YXRpb24uDQpidWlsZCA8LSBmdW5jdGlvbihwYXJtKSB7DQogIGRsbU1vZFBvbHkob3JkZXIgPSAyLCBkViA9IGV4cChwYXJtWzFdKSwgZFcgPSBjKGV4cChwYXJtWzJdKSxleHAocGFybVszXSkpKSArIGRsbU1vZFRyaWcocyA9IDcsIGRWID0gMCwgZFc9ZXhwKHBhcm1bNF0pKQ0KfQ0KZml0IDwtIGRsbU1MRShsb2cuY2hvYywgcmVwKDAsNCksIGJ1aWxkKQ0KZml0JGNvbnZlcmdlbmNlDQoNCkJJQy4ybmQuc2Vhc2ZhY3RvciA8LSAgMiAqICBmaXQkdmFsdWUgKyBsZW5ndGgoZml0JHBhcikgKiBsb2cobGVuZ3RoKGxvZy5jaG9jKSkNCnByaW50KCJCSUMiKQ0KQklDLjJuZC5zZWFzZmFjdG9yDQoNCm1vZGVsMiA8LSBidWlsZChmaXQkcGFyKSAgI1RoaXMgaXMgcGFydCB3aGVyZSBoZSB0YWtlcyB0aGUgbW9kZWwgcGFyYW1ldGVycw0KcHJpbnQoIk9ic2VydmF0aW9uYWwgbm9pc2UgZnJvbSBNTEUiKQ0KbW9kZWwyJFYNCnByaW50KCJJbm5vdmF0aW9uIHZhcmlhbmNlIG1hdHJpeCBkaWFnb25hbCBlbGVtZW50cyBmcm9tIE1MRSIpDQptb2RlbDIkV1sxOjIsMToyXQ0KDQpgYGANCg0KYGBge3J9DQpjaG9jZmlsdDIgPC0gZGxtRmlsdGVyKGxvZy5jaG9jLCBtb2RlbDIpDQoNCmNvdi5maWx0IDwtIHdpdGgoY2hvY2ZpbHQyLCBkbG1TdmQydmFyKFUuQywgRC5DKSkNCg0Kc2Vhcy50ZXJtID0gMg0KDQpzZC5zZWFzb25hbGl0eS5maWx0MiA8LSByZXAoTkEsbGVuZ3RoKGNvdi5maWx0KSkNCmZvcihpIGluIDE6bGVuZ3RoKGNvdi5maWx0KSkgc2Quc2Vhc29uYWxpdHkuZmlsdDJbaV0gPSBzcXJ0KGNvdi5maWx0W1tpXV1bc2Vhcy50ZXJtLHNlYXMudGVybV0pDQpzZC5zZWFzb25hbGl0eS5maWx0MiA9IHRzKHNkLnNlYXNvbmFsaXR5LmZpbHQyWy0oMTo4KV0sZnJlcXVlbmN5PTcpDQpzZWFzb25hbGl0eS5maWx0MiA9IHRzKGNob2NmaWx0MiRtWy0oMTo4KSxzZWFzLnRlcm1dLGZyZXF1ZW5jeT03KQ0KbGwgPSBzZWFzb25hbGl0eS5maWx0MiAtIDEuOTYgKiBzZC5zZWFzb25hbGl0eS5maWx0Mg0KdWwgPSBzZWFzb25hbGl0eS5maWx0MiArIDEuOTYgKiBzZC5zZWFzb25hbGl0eS5maWx0Mg0KbGxpbSA9IG1pbihsbCkNCnVsaW0gPSBtYXgodWwpDQpwbG90KHNlYXNvbmFsaXR5LmZpbHQyLHlsaW09YyhsbGltLHVsaW0pKQ0KbGluZXMobGwsbHR5PTIsY29sPSJncmVlbiIpDQpsaW5lcyh1bCxsdHk9Mixjb2w9ImdyZWVuIikNCmxsaW0gPSBtaW4oYyhtaW4oc2Vhc29uYWxpdHkuZmlsdDIvc2Quc2Vhc29uYWxpdHkuZmlsdDIpLC0xLjk2KSkNCnVsaW0gPSBtYXgoYyhtYXgoc2Vhc29uYWxpdHkuZmlsdDIvc2Quc2Vhc29uYWxpdHkuZmlsdDIpLDEuOTYpKQ0KcGxvdChzZWFzb25hbGl0eS5maWx0Mi9zZC5zZWFzb25hbGl0eS5maWx0Mix5bGltPWMobGxpbSx1bGltKSkNCmFibGluZShoPTEuOTYsbHR5PTIpDQphYmxpbmUoaD0tMS45NixsdHk9MikNCg0KYGBgDQoNCmBgYHtyfQ0Kc2Vhcy50ZXJtID0gNA0KDQpzZC5zZWFzb25hbGl0eS5maWx0MiA8LSByZXAoTkEsbGVuZ3RoKGNvdi5maWx0KSkNCmZvcihpIGluIDE6bGVuZ3RoKGNvdi5maWx0KSkgc2Quc2Vhc29uYWxpdHkuZmlsdDJbaV0gPSBzcXJ0KGNvdi5maWx0W1tpXV1bc2Vhcy50ZXJtLHNlYXMudGVybV0pDQpzZC5zZWFzb25hbGl0eS5maWx0MiA9IHRzKHNkLnNlYXNvbmFsaXR5LmZpbHQyWy0oMTo4KV0sZnJlcXVlbmN5PTcpDQpzZWFzb25hbGl0eS5maWx0MiA9IHRzKGNob2NmaWx0MiRtWy0oMTo4KSxzZWFzLnRlcm1dLGZyZXF1ZW5jeT03KQ0KbGwgPSBzZWFzb25hbGl0eS5maWx0MiAtIDEuOTYgKiBzZC5zZWFzb25hbGl0eS5maWx0Mg0KdWwgPSBzZWFzb25hbGl0eS5maWx0MiArIDEuOTYgKiBzZC5zZWFzb25hbGl0eS5maWx0Mg0KbGxpbSA9IG1pbihsbCkNCnVsaW0gPSBtYXgodWwpDQpwbG90KHNlYXNvbmFsaXR5LmZpbHQyLHlsaW09YyhsbGltLHVsaW0pKQ0KbGluZXMobGwsbHR5PTIsY29sPSJncmVlbiIpDQpsaW5lcyh1bCxsdHk9Mixjb2w9ImdyZWVuIikNCmxsaW0gPSBtaW4oYyhtaW4oc2Vhc29uYWxpdHkuZmlsdDIvc2Quc2Vhc29uYWxpdHkuZmlsdDIpLC0xLjk2KSkNCnVsaW0gPSBtYXgoYyhtYXgoc2Vhc29uYWxpdHkuZmlsdDIvc2Quc2Vhc29uYWxpdHkuZmlsdDIpLDEuOTYpKQ0KcGxvdChzZWFzb25hbGl0eS5maWx0Mi9zZC5zZWFzb25hbGl0eS5maWx0Mix5bGltPWMobGxpbSx1bGltKSkNCmFibGluZShoPTEuOTYsbHR5PTIpDQphYmxpbmUoaD0tMS45NixsdHk9MikNCg0KDQoNCnNlYXMudGVybSA9IDYNCg0Kc2Quc2Vhc29uYWxpdHkuZmlsdDIgPC0gcmVwKE5BLGxlbmd0aChjb3YuZmlsdCkpDQpmb3IoaSBpbiAxOmxlbmd0aChjb3YuZmlsdCkpIHNkLnNlYXNvbmFsaXR5LmZpbHQyW2ldID0gc3FydChjb3YuZmlsdFtbaV1dW3NlYXMudGVybSxzZWFzLnRlcm1dKQ0Kc2Quc2Vhc29uYWxpdHkuZmlsdDIgPSB0cyhzZC5zZWFzb25hbGl0eS5maWx0MlstKDE6OCldLGZyZXF1ZW5jeT03KQ0Kc2Vhc29uYWxpdHkuZmlsdDIgPSB0cyhjaG9jZmlsdDIkbVstKDE6OCksc2Vhcy50ZXJtXSxmcmVxdWVuY3k9NykNCmxsID0gc2Vhc29uYWxpdHkuZmlsdDIgLSAxLjk2ICogc2Quc2Vhc29uYWxpdHkuZmlsdDINCnVsID0gc2Vhc29uYWxpdHkuZmlsdDIgKyAxLjk2ICogc2Quc2Vhc29uYWxpdHkuZmlsdDINCmxsaW0gPSBtaW4obGwpDQp1bGltID0gbWF4KHVsKQ0KcGxvdChzZWFzb25hbGl0eS5maWx0Mix5bGltPWMobGxpbSx1bGltKSkNCmxpbmVzKGxsLGx0eT0yLGNvbD0iZ3JlZW4iKQ0KbGluZXModWwsbHR5PTIsY29sPSJncmVlbiIpDQpsbGltID0gbWluKGMobWluKHNlYXNvbmFsaXR5LmZpbHQyL3NkLnNlYXNvbmFsaXR5LmZpbHQyKSwtMS45NikpDQp1bGltID0gbWF4KGMobWF4KHNlYXNvbmFsaXR5LmZpbHQyL3NkLnNlYXNvbmFsaXR5LmZpbHQyKSwxLjk2KSkNCnBsb3Qoc2Vhc29uYWxpdHkuZmlsdDIvc2Quc2Vhc29uYWxpdHkuZmlsdDIseWxpbT1jKGxsaW0sdWxpbSkpDQphYmxpbmUoaD0xLjk2LGx0eT0yKQ0KYWJsaW5lKGg9LTEuOTYsbHR5PTIpDQoNCg0Kc2Vhcy50ZXJtID0gOA0KDQpzZC5zZWFzb25hbGl0eS5maWx0MiA8LSByZXAoTkEsbGVuZ3RoKGNvdi5maWx0KSkNCmZvcihpIGluIDE6bGVuZ3RoKGNvdi5maWx0KSkgc2Quc2Vhc29uYWxpdHkuZmlsdDJbaV0gPSBzcXJ0KGNvdi5maWx0W1tpXV1bc2Vhcy50ZXJtLHNlYXMudGVybV0pDQpzZC5zZWFzb25hbGl0eS5maWx0MiA9IHRzKHNkLnNlYXNvbmFsaXR5LmZpbHQyWy0oMTo4KV0sZnJlcXVlbmN5PTcpDQpzZWFzb25hbGl0eS5maWx0MiA9IHRzKGNob2NmaWx0MiRtWy0oMTo4KSxzZWFzLnRlcm1dLGZyZXF1ZW5jeT03KQ0KbGwgPSBzZWFzb25hbGl0eS5maWx0MiAtIDEuOTYgKiBzZC5zZWFzb25hbGl0eS5maWx0Mg0KdWwgPSBzZWFzb25hbGl0eS5maWx0MiArIDEuOTYgKiBzZC5zZWFzb25hbGl0eS5maWx0Mg0KbGxpbSA9IG1pbihsbCkNCnVsaW0gPSBtYXgodWwpDQpwbG90KHNlYXNvbmFsaXR5LmZpbHQyLHlsaW09YyhsbGltLHVsaW0pKQ0KbGluZXMobGwsbHR5PTIsY29sPSJncmVlbiIpDQpsaW5lcyh1bCxsdHk9Mixjb2w9ImdyZWVuIikNCmxsaW0gPSBtaW4oYyhtaW4oc2Vhc29uYWxpdHkuZmlsdDIvc2Quc2Vhc29uYWxpdHkuZmlsdDIpLC0xLjk2KSkNCnVsaW0gPSBtYXgoYyhtYXgoc2Vhc29uYWxpdHkuZmlsdDIvc2Quc2Vhc29uYWxpdHkuZmlsdDIpLDEuOTYpKQ0KcGxvdChzZWFzb25hbGl0eS5maWx0Mi9zZC5zZWFzb25hbGl0eS5maWx0Mix5bGltPWMobGxpbSx1bGltKSkNCmFibGluZShoPTEuOTYsbHR5PTIpDQphYmxpbmUoaD0tMS45NixsdHk9MikNCg0KDQoNCg0KDQojIyMjIyMjIyMjIyMjIyMjIyMjDQojICAgRm9yZWNhc3RpbmcgICAjDQojIyMjIyMjIyMjIyMjIyMjIyMjDQoNCnByZWRpY3Rpb25zIDwtIGRsbUZvcmVjYXN0KGNob2NmaWx0Miwgbj0yOCkNCg0KbGwgPSBwcmVkaWN0aW9ucyRmIC0gMS45NiAqIHNxcnQodW5saXN0KHByZWRpY3Rpb25zJFEpKQ0KdWwgPSBwcmVkaWN0aW9ucyRmICsgMS45NiAqIHNxcnQodW5saXN0KHByZWRpY3Rpb25zJFEpKQ0KcGxvdChsb2cuY2hvYywgeGxhYiA9ICIiLCBjb2wgPSAiZGFya2dyZXkiLHhsaW09YygyNTAsMzAwKSxsd2Q9MikNCiNwbG90KGxvZy5jaG9jLCB4bGFiID0gIiIsIGNvbCA9ICJkYXJrZ3JleSIseGxpbT1jKDE5NTgsMjAwMCksIHlsaW09YygxMDAwLDEwMDAwKSxsd2Q9MikNCmxpbmVzKHByZWRpY3Rpb25zJGYsIGNvbD0icmVkIixsd2Q9MikNCmxpbmVzKGxsLGx0eT0yLCBjb2w9ImdyZWVuIixsd2Q9MikNCmxpbmVzKHVsLGx0eT0yLCBjb2w9ImdyZWVuIixsd2Q9MikNCg0KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjDQojICAgICAgICAgIE9uZS1zdGVwIGFoZWFkIGZvcmVjYXN0IGVycm9yIGZvciB0aGUgbGFzdCA5IHllYXJzICAgICAgICAgICAgICAgICMNCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIw0KcHJpbnQoIiBNZWFuIGFic29sdXRlIGZvcmVjYXN0IGVycm9yIikNCiMgTWVhbiBhYnNvbHV0ZSBmb3JlY2FzdCBlcnJvciAoTUFFKQ0KbWVhbihhYnMoY2hvY2ZpbHQyJGZbMTgwMDoxOTA4XSAtIGxvZy5jaG9jWzE4MDA6MTkwOF0pKQ0KDQpwcmludCgiIE1lYW4gc3F1YXJlZCBmb3JlY2FzdCBlcnJvciAoTVNFKSIpDQojIE1lYW4gc3F1YXJlZCBmb3JlY2FzdCBlcnJvciAoTVNFKQ0KbWVhbigoY2hvY2ZpbHQyJGZbMTgwMDoxOTA4XSAtIGxvZy5jaG9jWzE4MDA6MTkwOF0pXjIpDQoNCnByaW50KCIgTWVhbiBhYnNvbHV0ZSBwZXJjZW50YWdlIGZvcmVjYXN0IGVycm9yIChNQVBFKSIpDQojIE1lYW4gYWJzb2x1dGUgcGVyY2VudGFnZSBmb3JlY2FzdCBlcnJvciAoTUFQRSkNCm1lYW4oYWJzKGNob2NmaWx0MiRmWzE4MDA6MTkwOF0gLSBsb2cuY2hvY1sxODAwOjE5MDhdKSAvIGxvZy5jaG9jWzE4MDA6MTkwOF0pDQoNCnBsb3QoY2hvY2ZpbHQyJGYseWxpbT1jKDEwLDExKSkNCmxpbmVzKGxvZy5jaG9jLGNvbD0iZ3JlZW4iKQ0KYGBgDQpgYGANCg0K